package com.easyj.admin.sys.serviceimpl;

import java.util.List;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import com.easyj.admin.sys.service.DepartmentService;
import com.easyj.admin.db.mapper.DepartmentMapper;
import com.easyj.base.common.imp.ImpExcutor;
import com.easyj.base.common.imp.ImpField;
import com.easyj.base.common.imp.ImpUtils;
import com.easyj.base.common.vo.tree.TreeNode;
import com.easyj.base.exception.EasyjException;
import com.easyj.base.exception.ImpValidException;
import com.easyj.base.exception.ValidateException;
import com.easyj.admin.db.model.Department;
import com.easyj.admin.db.model.ImpResult;



/**
 * 部门管理，逻辑删除
 * @author hjf
 *
 */
@Service
@CacheConfig(cacheNames = "department")
public class DepartmentServiceImpl implements DepartmentService{
	
	@Autowired
	private  DepartmentMapper departmentMapper;

	
   	public List<Department> findList(Department filter){
   		filter.setDeleted(false);
        return departmentMapper.selectByExample(filter);
    }
   	
   	
   	public List<TreeNode> getAllTree(){
   		Department filter=new Department(); //find all
   		List<Department> list= departmentMapper.selectByExample(filter);
   		List<TreeNode>  roots= new ArrayList<TreeNode>();
   		
   		if(list==null||list.size()==0) {
   			return roots;
   		}
   		
   		List<TreeNode>  nodes= new ArrayList<TreeNode>();
		for(Department p:list){
			TreeNode node=new TreeNode();
			node.setLabel(p.getName());
			node.setCode(p.getCode());
			node.setId(p.getId());
			node.setParentId(p.getParentId());
			node.setType(p.getType());
			node.setDisabled(p.getDeleted());
			nodes.add(node);
		}
		
		for(TreeNode c:nodes){	
			for(TreeNode p: nodes){
				if(p!=c && c.getParentId().longValue()==p.getId().longValue()){
					if(p.getChildren()==null){
						p.setChildren(new ArrayList<TreeNode>());
					}
					p.getChildren().add(c);
					break;
				}
			}			
			
		}
		
		for(TreeNode c:nodes){
			if(c.getParentId().longValue()==0){ //root parentid==0
				roots.add(c);
			}
		}
   		
		return roots;
   	}
   	
   	public List<TreeNode> getTree(boolean onlyComapny){
   		Department filter=new Department(); //find all used
   		filter.setDeleted(false);
   		if(onlyComapny) {
   			filter.setType(1);
   		}
   		List<Department> list= departmentMapper.selectByExample(filter);
   		List<TreeNode>  roots= new ArrayList<TreeNode>();
   		
   		if(list==null||list.size()==0) {
   			return roots;
   		}
   		
   		List<TreeNode>  nodes= new ArrayList<TreeNode>();
		for(Department p:list){
			TreeNode node=new TreeNode();
			node.setLabel(p.getName());
			node.setId(p.getId());
			node.setParentId(p.getParentId());
			nodes.add(node);
		}
		
		for(TreeNode c:nodes){	
			for(TreeNode p: nodes){
				if(p!=c && c.getParentId().longValue()==p.getId().longValue()){
					if(p.getChildren()==null){
						p.setChildren(new ArrayList<TreeNode>());
					}
					p.getChildren().add(c);
					break;
				}
			}			
			
		}
		
		for(TreeNode c:nodes){
			if(c.getParentId().longValue()==0){ //root parentid==0
				roots.add(c);
			}
		}
   		
		return roots;
   	}
   	/**
   	 * 后代树，withSelf 是否包含自已
   	 * @param parentId
   	 * @param withSelf 是否包含自已
   	 * @return
   	 */
	public List<TreeNode> getDesendantTree(Long deptId,boolean withSelf){
   		Department filter=new Department(); //find all used
   		filter.setDeleted(false);
   		
   		List<Department> list= departmentMapper.selectByExample(filter);
   		List<TreeNode>  roots= new ArrayList<TreeNode>();
   		
   		if(list==null||list.size()==0) {
   			return roots;
   		}
   		
   		List<TreeNode>  nodes= new ArrayList<TreeNode>();
		for(Department p:list){
			TreeNode node=new TreeNode();
			node.setLabel(p.getName());
			node.setId(p.getId());
			node.setParentId(p.getParentId());
			nodes.add(node);
		}
		
		for(TreeNode c:nodes){	
			for(TreeNode p: nodes){
				if(p!=c && c.getParentId().longValue()==p.getId().longValue()){
					if(p.getChildren()==null){
						p.setChildren(new ArrayList<TreeNode>());
					}
					p.getChildren().add(c);
					break;
				}
			}			
			
		}
		if(deptId==null) {
			for(TreeNode c:nodes){
				if(c.getParentId().longValue()==0){ //root parentid==0
					roots.add(c);
				}
			}
		}else {
			if(withSelf) {
				for(TreeNode c:nodes){
					if(c.getId().longValue()==deptId){ 
						roots.add(c);
						break;
					}
				}
			}else {
				for(TreeNode c:nodes){
					if(c.getParentId().longValue()==deptId){ //root parentid==0
						roots.add(c);
					}
				}
			}
		}	
		return roots;
   	}
   	/**
   	 * 按权限限定的树
   	 * @return
   	 */
   	public List<TreeNode> getLimitTree(List<Long> limitIds,boolean onlyComapny){
   		List<TreeNode>  roots= new ArrayList<TreeNode>();
   		
   	//find all used,list=all
   		Department filter=new Department(); 
   		filter.setDeleted(false);
   		if(onlyComapny) {
   			filter.setType(1);
   		}
   		List<Department> list= departmentMapper.selectByExample(filter);	
   		if(list==null||list.size()==0) {
   			return roots;
   		}
   		
   		
   		List<Department> limitList=new ArrayList<Department>();
   		for(Long id: limitIds) {
   			for(Department dept:list) {
   				if(id.longValue()==dept.getId().longValue()) {
   					dept.setDeleted(false);
   					limitList.add(dept);
   					list.remove(dept);  				   					
   					break;
   				}
   			}
   		}
   		
   		//get limit parent
   		List<Department> plist=new ArrayList<Department>();
   		for(Department dept: limitList) {
   			Long pid=dept.getParentId();
   			while(pid.longValue()!=0l && !limitIds.contains(pid)) {//not root 
   				Department parent=null;
   				for(Department f:list) {
   					if(f.getId()==pid.longValue()) {
   						parent=f;
   						break;
   					}
   				}
   				if(parent!=null) {
   					parent.setDeleted(true);
   					plist.add(parent);
   					limitIds.add(parent.getId());
   					//contine find parent
   					pid=parent.getParentId();
   				}
   			}
   		}
   		
   		List<TreeNode>  nodes= new ArrayList<TreeNode>();
   		limitList.addAll(plist);
   		//sort
		Collections.sort(limitList, new Comparator<Department>() {
            public int compare(Department o1, Department o2) {   	            	
            	  return  o1.getCode().compareTo(o2.getCode());               
            }
		 });  
   		
   		for(Department dept: limitList) {
   			TreeNode node=new TreeNode();
			node.setLabel(dept.getName());
			node.setId(dept.getId());
			node.setParentId(dept.getParentId());
			node.setDisabled(dept.getDeleted()); //设为不可选
			nodes.add(node);
   		}
   		
		
		
		//build tree
		for(TreeNode c:nodes){	
			for(TreeNode p: nodes){
				if(p!=c && c.getParentId().longValue()==p.getId().longValue()){
					if(p.getChildren()==null){
						p.setChildren(new ArrayList<TreeNode>());
					}
					p.getChildren().add(c);
					break;
				}
			}					
		}
		
		for(TreeNode c:nodes){
			if(c.getParentId().longValue()==0l){ //root parentid==0
				roots.add(c);
			}
		}
   		
		return roots;
   	}
   	/**
   	 * 返回所有下级Id和及自己Id,没有返回null; deleted=false,表示只返回正常的，不包含停用的
   	 * @param id
   	 * @return
   	 */
   	public List<Long> getDesendantIds(Long id,boolean deleted){
   		List<Long> rtn=new ArrayList<Long>();
   		Department filter=new Department(); //find all
   		if(!deleted) {
   		   filter.setDeleted(false);
   		}
   		List<Department> list= departmentMapper.selectByExample(filter);
   		Department find=null;
   		if(list!=null) {
   			for(Department d:list) {
   				if(d.getId().longValue()==id.longValue()) {
   					find=d;
   					break;
   				}
   			}
   		}
   		if(find==null) {
   			return null;
   		}
   		
   		rtn.add(find.getId());
   		rtFindChildren(rtn,list,find);
   		return rtn;
   	}
   	
	public List<Long> getDesendantCompanyIds(Long id){
   		List<Long> rtn=new ArrayList<Long>();
   		Department filter=new Department(); //find all
   		filter.setType(1);
   		filter.setDeleted(false);
   		
   		List<Department> list= departmentMapper.selectByExample(filter);
   		Department find=null;
   		if(list!=null) {
   			for(Department d:list) {
   				if(d.getId().longValue()==id.longValue()) {
   					find=d;
   					break;
   				}
   			}
   		}
   		if(find==null) {
   			return null;
   		}
   		
   		rtn.add(find.getId());
   		rtFindChildren(rtn,list,find);
   		return rtn;
   	}
  
   	
   	private void rtFindChildren(List<Long> rtn,List<Department> list,Department cur) {
   		for(Department d:list) {
   			if(d.getParentId().longValue()==cur.getId().longValue()) {
   				rtn.add(d.getId());
   				rtFindChildren(rtn,list,d);
   			}
   			
   		}
   	}

   	@Cacheable(key="#id")
	public Department findById(Long id){
		Department entity= departmentMapper.selectByPrimaryKey(id);
		return entity;
	}
	

	@Transactional
	public int  insertEntity(Department entity) throws ValidateException{
		if(departmentMapper.selectUniqueByUnique(entity.getCode())!=null) {
			throw new ValidateException(EasyjException.SameCode,"已有相同编号的部门或公 司！");
		}
		
		if(entity.getParentId()!=0l) {
			Department parent=departmentMapper.selectByPrimaryKey(entity.getParentId());
			if(parent==null) {
				throw new ValidateException("数据错误！");
			}
			if(entity.getType().intValue()==1) {			
				if(parent.getType().intValue()!=1) {
					throw new ValidateException("不可以在部门下建立下级公 司！");
				}
			}
			//companyName-----------
			if(entity.getType().intValue()==2) {
				if(parent.getType().intValue()==1) {
					entity.setCompanyId(parent.getId());
					entity.setCompanyName(parent.getName());
				}else {
					entity.setCompanyId(parent.getCompanyId());
					entity.setCompanyName(parent.getCompanyName());
				}
			}
			
		}
		entity.setDeleted(false);
	    entity.setCreateTime(new Date());
		int rtn= departmentMapper.insert(entity);
		return rtn;
	}
	
	@Transactional
	@CacheEvict(key="#entity.id")
	public int  updateEntity(Department entity) throws ValidateException{
		Department find= departmentMapper.selectByPrimaryKey(entity.getId());
		Department find_code=departmentMapper.selectUniqueByUnique(entity.getCode());
	    if(find_code!=null && find_code.getId().longValue()!=entity.getId().longValue()) {
			throw new ValidateException(EasyjException.SameCode,"已有相同编号的部门！");
		}
	    find.setModifyTime(new Date()); 
	    find.setCode(entity.getCode());
	    find.setName(entity.getName());
	    find.setOperatorId(entity.getOperatorId());	    
		int rtn= departmentMapper.updateByPrimaryKey(find);
		
		//update companyName
		if(find.getType().intValue()==1) {
		  List<Long> ds=this.getDesendantIdsOnly2(find);
		  for(Long ds_id:ds) {
			  Department dep=new Department();
			  dep.setId(ds_id);
			  dep.setCompanyId(find.getId());
			  dep.setCompanyName(find.getName());
			  updateSelective(dep);
		  }
		}
	
		return rtn;
	}
	
	
	private int  updateSelective(Department entity) throws ValidateException{
		int rtn= departmentMapper.updateByPrimaryKeySelective(entity);
		return rtn;
	} 
	

	@Transactional
	@CacheEvict(key="#id")
	public int  changeParent(Long id,Long parentId,Long operatorId) throws ValidateException{
		Department find= departmentMapper.selectByPrimaryKey(id);
		if(find==null) {
			throw new ValidateException("数据不符合要求！");
		}
		int rtn=0;
		Department parent=null;
		if(parentId==null||parentId==0l) {
			 if(find.getType().intValue()==2) {
				throw new ValidateException("部门不能改为一级！");
			 } 
			 if(find.getParentId().longValue()==0l){
				 return 0;
			 }
	    	 find.setParentId(0l);
	    	 find.setModifyTime(new Date()); 
			 find.setOperatorId(operatorId);	
			 rtn= departmentMapper.updateByPrimaryKey(find);
	     } else {
		    parent= departmentMapper.selectByPrimaryKey(parentId);
			if(parent==null) {
				throw new ValidateException("数据不符合要求！");
			}
			if(find.getParentId().longValue()==parentId.longValue()) {
				return 0;
			}
		 
			List<Long> des=getDesendantIds(find.getId(),true);//
			if(des.contains(parentId)) {
				throw new ValidateException("不能把上级设为其下级或自己！");
			}
			if(find.getType().intValue()==1) {
				if(parent.getType().intValue()==2) {
					throw new ValidateException("公司上级不能为部门！");
				}
			}
		    find.setModifyTime(new Date()); 
		    find.setParentId(parentId);
		    find.setOperatorId(operatorId);	
		    
		  
			if(find.getType().intValue()==2) {
				//companyName-----------
				Long companyId;
				String companyName;
				if(parent.getType().intValue()==1) {
					companyId=parent.getId();
					companyName=parent.getName();
				}else {
					companyId=parent.getCompanyId();
					companyName=parent.getCompanyName();
				}
				find.setCompanyId(companyId);
				find.setCompanyName(companyName);
				rtn= departmentMapper.updateByPrimaryKey(find);
				
				List<Long> ds=this.getDesendantIdsOnly2(find);
				  for(Long ds_id:ds) {
					  Department dep=new Department();
					  dep.setId(ds_id);
					  dep.setCompanyId(companyId);
					  dep.setCompanyName(companyName);
				  }
			}else {
				rtn= departmentMapper.updateByPrimaryKey(find);
			}

		
	     }
		
				
		return rtn;
	}

	
	public List<Department> getPermCompanys(List<Long> limitIds){
   		List<Department>  roots= new ArrayList<Department>();
   		
   		Department filter=new Department(); 
   		filter.setDeleted(false);
   		filter.setType(1);
   		
   		List<Department> list= departmentMapper.selectByExample(filter);	
   		if(list==null||list.size()==0) {
   			return roots;
   		}
   		if(limitIds==null||limitIds.size()==0) {
   			return list;
   		}
   		
   		
   		for(Long id: limitIds) {
   			for(Department dept:list) {
   				if(id.longValue()==dept.getId().longValue()) {
   					roots.add(dept);			   					
   					break;
   				}
   			}
   		}

		return roots;
   	}
	
	
	
	 public Department selectUniqueByUnique(String code) {
		 return departmentMapper.selectUniqueByUnique(code);
	 }
	
	
	
	@Transactional
	@CacheEvict(key="#id")
	public int  deleteById(Long id,Long operatorId){
		return departmentMapper.deleteByPrimaryKey(id);
	}

	@Transactional
	@CacheEvict(key="#id")
	public int  disabledById(Long id,Long operatorId){
		Department entity=departmentMapper.selectByPrimaryKey(id);
		entity.setModifyTime(new Date()); 
		entity.setOperatorId(operatorId);
		//--
		entity.setDeleted(true);
		return departmentMapper.updateByPrimaryKeySelective(entity);
	}
	
	@Transactional
	@CacheEvict(key="#id")
	public int  enableById(Long id,Long operatorId){
		Department entity=departmentMapper.selectByPrimaryKey(id);
		entity.setModifyTime(new Date()); 
		entity.setOperatorId(operatorId);
		//--
		entity.setDeleted(false);
		return departmentMapper.updateByPrimaryKeySelective(entity);		
	}
	
	
	private List<Long> getDesendantIdsOnly2(Department find){
   		List<Long> rtn=new ArrayList<Long>();
   		Department filter=new Department(); //find all
   		List<Department> list= departmentMapper.selectByExample(filter);
   		rtFindChildrenOnly2(rtn,list,find);
   		return rtn;
   	}
	
 	private void rtFindChildrenOnly2(List<Long> rtn,List<Department> list,Department cur) {
   		for(Department d:list) {
   			if(d.getParentId().longValue()==cur.getId().longValue()&& d.getType().intValue()==2) {
   				rtn.add(d.getId());
   				rtFindChildrenOnly2(rtn,list,d);
   			}
   			
   		}
   	}
	
	
 	/**
	 * import from excel
	 * @param excelfile
	 * @param operatorId
	 * @return
	 * @throws Exception
	 */
	public ImpResult importExcel(MultipartFile excelfile,Long operatorId) throws Exception{
		ValidatorFactory factory=Validation.buildDefaultValidatorFactory();
		final Validator validator =factory.getValidator(); 
		ImpExcutor<Department> impExcutor=new ImpExcutor<Department>("组织架构",operatorId){

				@Override
				protected Department getAndValidateBean(Row row) throws ImpValidException {
					HashMap<String,String> errmap=new HashMap<String,String>();
					//set bean value
					Department bean=new Department();
					bean.setDeleted(false);//status.........
					//直接设值
					//ImpUtils.setObjectValueByRow(row, fields, bean);
					//设值，对分类特别处理
					String 	parentCode=null;
					String  type=null;
					for(ImpField f :fields){
						Cell cell=row.getCell(f.getPosition());
						if(cell==null){
							continue;
						}
						try {
							if(f.getName().equals("parentCode")) {
								parentCode=(String)ImpUtils.getCellValue(cell,f);
							}else if(f.getName().equals("type")) {
								type=(String)ImpUtils.getCellValue(cell,f);
							}
							else if(f.getPosition()!=null){
				                Object value=ImpUtils.getCellValue(cell,f);
								PropertyUtils.setProperty(bean, f.getName(),value);
							}
						}catch(Exception ex) {
							errmap.put(f.getName(), "数据不符合规范，取值失败");
						}
					}
					if(errmap.size()>0) {
						throw new ImpValidException("取值失败",errmap);
					}
					
					//get parentId...
					if(StringUtils.isNotBlank(parentCode)) {
						Department  ps= selectUniqueByUnique(parentCode);
						 if(ps==null) {
							 errmap.put("parentCode", "上级部门编号不存在");
						 }else {
							 bean.setParentId(ps.getId());
						 }
					}else {
						bean.setParentId(0l);//top =0
					}
					if("公司".equals(type)) {
						bean.setType(1);
					}else if("部门".equals(type)) {
						bean.setType(2);
					}else {
						errmap.put("type", "类型不符合要求");
					}
					
					//valid
					Set<ConstraintViolation<Department>> constraintViolations = validator.validate(bean);
					if(CollectionUtils.isNotEmpty(constraintViolations)){
						for(ConstraintViolation<Department> e:constraintViolations){
							if(e.getPropertyPath()!=null){
								errmap.put(e.getPropertyPath().toString(), e.getMessage());
							}
						}
						
					}
					//has error then throw excepton
	                if(errmap.size()>0) {
	                	throw new ImpValidException("数据不符合规范",errmap);
	                }
					
					return bean;
				}
	
				@Override
				protected int save(Department bean) throws Exception{
					try {
					  bean.setOperatorId(operatorId);	
					  return insertEntity(bean);	
					}catch(ValidateException e ) {
						HashMap<String,String> errmap=new HashMap<String,String>();
						errmap.put("code",  "已有相同的编号！");
						throw new ImpValidException("",errmap);
					}
				}
		};
	
         ImpResult rtn=impExcutor.initImport(excelfile, "department.xml");
		
		//异步导入
		runImportData(impExcutor);
		
		//更新公司名（冗余）。。。。
		Department filter=new Department(); //find all
   		filter.setType(1);
   		List<Department> list= departmentMapper.selectByExample(filter);
   		if(list!=null) {
   			for(Department find:list) {
				if(find.getType().intValue()==1) {
				  List<Long> ds=this.getDesendantIdsOnly2(find);
				  for(Long ds_id:ds) {
					  Department dep=new Department();
					  dep.setId(ds_id);
					  dep.setCompanyId(find.getId());
					  dep.setCompanyName(find.getName());
					  updateSelective(dep);
				  }
				}
   			}	
   		}		
		return rtn;
	}
	 @Async
	private void runImportData(ImpExcutor<Department> excutor) {
		excutor.execImport();
	}
}
